package com.insight.common.system.base.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.shiro.SecurityUtils;
import com.insight.common.api.vo.Result;
import com.insight.common.system.query.QueryGenerator;
import com.insight.common.system.util.ExcelUtilJeecg;
import com.insight.common.system.util.ZipEncryUtilsJeecg;
import com.insight.common.system.vo.LoginUser;
import com.insight.common.util.oConvertUtils;
import org.jeecgframework.poi.excel.ExcelExportUtil;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.entity.enmus.ExcelType;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @Description: Controller基类
 * @Author: dangzhenghui@163.com
 * @Date: 2019-4-21 8:13
 * @Version: 1.0
 */
@Slf4j
public class JeecgController<T, S extends IService<T>> {
    @Autowired
    protected  S service;
    @Autowired
    private ZipEncryUtilsJeecg zipEncryUtils;

    /**
     * 导出excel
     *
     * @param request
     */
    protected ModelAndView exportXls(HttpServletRequest request, T object, Class<T> clazz, String title) {
        // Step.1 组装查询条件
        Object principal = SecurityUtils.getSubject().getPrincipal();
        if (principal == null) {
            // 可选择继续执行或设置默认值，这里选择继续
            log.warn("当前用户未登录，导出操作将不基于用户权限");
        }
        // 注意：LoginUser 强转仍可能 ClassCastException，但 NPE 风险已控

        QueryWrapper<T> queryWrapper = QueryGenerator.initQueryWrapper(object, request.getParameterMap());

        // Step.2 获取导出数据
        List<T> pageList = service.list(queryWrapper);
        if (pageList == null) {
            pageList = Collections.emptyList();
        }

        List<T> exportList = null;

        // 过滤选中数据
        String selections = request.getParameter("selections");
        if (oConvertUtils.isNotEmpty(selections)) {
            // 此时 selections 不为 null 且非空
            String[] selectionArray = selections.split(",");
            List<String> selectionList = Arrays.asList(selectionArray);
            exportList = pageList.stream()
                    .filter(item -> item != null) // 过滤 null item
                    .filter(item -> {
                        try {
                            String id = getId(item);
                            return id != null && selectionList.contains(id);
                        } catch (Exception e) {
                            log.warn("获取对象 ID 失败，跳过该条记录", e);
                            return false;
                        }
                    })
                    .collect(Collectors.toList());
        } else {
            exportList = pageList;
        }

        if (exportList == null) {
            exportList = Collections.emptyList();
        }

        // Step.3 AutoPoi 导出Excel
        ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
        mv.addObject(NormalExcelConstants.FILE_NAME, title);
        mv.addObject(NormalExcelConstants.CLASS, clazz);
        try {
            mv.addObject(NormalExcelConstants.PARAMS, new ExportParams(title + "报表", title));
        } catch (Exception e) {
            log.error("创建导出参数失败", e);
        }
        mv.addObject(NormalExcelConstants.DATA_LIST, exportList);
        return mv;
    }

    //导出加密
    protected void exportXlsZip(HttpServletRequest request, HttpServletResponse response, T object, Class<T> clazz, String title, String pwd, Boolean isEncry) {

        QueryWrapper<T> queryWrapper = QueryGenerator.initQueryWrapper(object, request.getParameterMap());
        if (title.equals("终端组织机构报表")) {
            queryWrapper.ne("name", "全部单位");
        }
        // Step.2 获取导出数据
        List<T> pageList = service.list(queryWrapper);
        List<T> exportList = null;

        // 过滤选中数据
        String selections = request.getParameter("selections");
        if (oConvertUtils.isNotEmpty(selections)) {
            List<String> selectionList = Arrays.asList(selections.split(","));
            exportList = pageList.stream().filter(item -> selectionList.contains(getId(item))).collect(Collectors.toList());
        } else {
            exportList = pageList;
        }
        Collections.reverse(exportList);
        zipPwd(response, clazz, title, pwd, isEncry, exportList);
    }

    protected List<T> selectionsFilter(HttpServletRequest request,List<T> list){
        // 过滤选中数据
        String selections = request.getParameter("selections");
        if (oConvertUtils.isNotEmpty(selections)) {
            List<String> selectionList = Arrays.asList(selections.split(","));
            list = list.stream().filter(item -> selectionList.contains(getId(item))).collect(Collectors.toList());
        }
        Collections.reverse(list);
        return list;
    }

    protected void saveXls(HttpServletRequest request, HttpServletResponse response
            , Class<T> clazz, String title, String pwd, Boolean isEncry, QueryWrapper<T> queryWrapper){
        // 获取导出数据
        List<T> pageList = service.list(queryWrapper);
        List<T> saveList = null;
        // 过滤选中数据
        String selections = request.getParameter("selections");
        if (oConvertUtils.isNotEmpty(selections)) {
            List<String> selectionList = Arrays.asList(selections.split(","));
            saveList = pageList.stream().filter(item -> selectionList.contains(getId(item))).collect(Collectors.toList());
        } else {
            saveList = pageList;
        }
        Collections.reverse(saveList);//实现对list的降序排序
        zipPwd(response, clazz, title, pwd, isEncry, saveList);
    }

    /**
     * Excel数据带有数据库字段条件限制的导出(加密格式)
     *
     * @param request
     * @param response
     * @param clazz
     * @param title
     * @param pwd
     * @param isEncry
     */
    protected void exportXlsZip(HttpServletRequest request, HttpServletResponse response
            , Class<T> clazz, String title, String pwd, Boolean isEncry, QueryWrapper<T> queryWrapper) {
        // 获取导出数据
        List<T> pageList = service.list(queryWrapper);
        List<T> exportList = null;
        // 过滤选中数据
        String selections = request.getParameter("selections");
        if (oConvertUtils.isNotEmpty(selections)) {
            List<String> selectionList = Arrays.asList(selections.split(","));
            exportList = pageList.stream().filter(item -> selectionList.contains(getId(item))).collect(Collectors.toList());
        } else {
            exportList = pageList;
        }
        Collections.reverse(exportList);
        zipPwd(response, clazz, title, pwd, isEncry, exportList);
    }

    protected void zipPwd(HttpServletResponse response, Class<T> clazz, String title, String pwd, Boolean isEncry, List<T> exportList) {
        ExportParams exportParams = new ExportParams();
        exportParams.setSheetName(title);

        Map<String, Object> deptDataMap = new HashMap<>();
        deptDataMap.put("title", exportParams);
        deptDataMap.put("entity", clazz);

        List<T> reversedList = new ArrayList<>(exportList);
        Collections.reverse(reversedList);
        deptDataMap.put("data", reversedList);

        List<Map<String, Object>> sheetsList = new ArrayList<>();
        sheetsList.add(deptDataMap);

        Workbook workbook = ExcelExportUtil.exportExcel(sheetsList, ExcelType.HSSF);

        try {
            if (Boolean.TRUE.equals(isEncry) && pwd != null && !pwd.trim().isEmpty()) {
                zipEncryUtils.excelEncry(title + ".xls", response, workbook, pwd);
            } else {
                ExcelUtilJeecg.downLoadExcel(title + ".xls", response, workbook);
            }
        } catch (Exception e) {
            log.error("导出文件失败", e);
        } finally {
            if (workbook != null) {
                try {
                    workbook.close();
                } catch (IOException e) {
                    log.warn("关闭 workbook 失败", e);
                }
            }
        }
    }

    protected void zipPwdWithExportFields(HttpServletResponse response, Class<T> clazz, String title, String pwd, Boolean isEncry, List<T> exportList, String exportFieldStr) {
        if (response == null || clazz == null || title == null || exportList == null) {
            return;
        }

        ExportParams exportParams = new ExportParams();
        exportParams.setSheetName(title);
        String[] exportFields = null;
        if (StringUtils.isNotEmpty(exportFieldStr)) {
            exportFields = exportFieldStr.split(",");
            // 去空格
            for (int i = 0; i < exportFields.length; i++) {
                exportFields[i] = exportFields[i].trim();
            }
        }

        Workbook workbook = null;
        try {
            workbook = ExcelExportUtil.exportExcel(exportParams, clazz, exportList, exportFields);

            if (Boolean.TRUE.equals(isEncry) && StringUtils.isNotBlank(pwd)) {
                zipEncryUtils.excelEncry(title + ".xls", response, workbook, pwd);
            } else {
                ExcelUtilJeecg.downLoadExcel(title + ".xls", response, workbook);
            }
        } catch (Exception e) {
            log.error("导出文件失败：{}", title, e);
        } finally {
            if (workbook != null) {
                try {
                    workbook.close();
                } catch (IOException e) {
                    log.warn("关闭 workbook 失败", e);
                }
            }
        }
    }

    protected ModelAndView exportXlsDept(HttpServletRequest request, T object, Class<T> clazz, String title) {
        // Step.1 组装查询条件
        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        if (sysUser == null) {
            throw new IllegalArgumentException("用户未登录");
        }

        QueryWrapper<T> queryWrapper = QueryGenerator.initQueryWrapper(object, request.getParameterMap());
        queryWrapper.ne("name", "全部单位");

        // Step.2 获取导出数据
        List<T> pageList = service.list(queryWrapper);
        List<T> exportList = pageList;

        String selections = request.getParameter("selections");
        if (oConvertUtils.isNotEmpty(selections)) {
            List<String> selectionList = Arrays.asList(selections.split(","));
            exportList = pageList.stream()
                    .filter(item -> {
                        String id = getId(item);
                        return id != null && selectionList.contains(id.trim());
                    })
                    .collect(Collectors.toList());
        }

        // Step.3 AutoPoi 导出Excel
        ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
        mv.addObject(NormalExcelConstants.FILE_NAME, title); //此处设置的filename无效 ,前端会重更新设置一下
        mv.addObject(NormalExcelConstants.CLASS, clazz);
        try {
            mv.addObject(NormalExcelConstants.PARAMS, new ExportParams(title + "报表", title));
        } catch (Exception e) {
            log.error("创建导出参数失败", e);
        }
        mv.addObject(NormalExcelConstants.DATA_LIST, exportList);
        return mv;

    }

    /**
     * 根据权限导出excel，传入导出字段参数
     *
     * @param request
     */
    protected ModelAndView exportXls(HttpServletRequest request, T object, Class<T> clazz, String title, String exportFields) {
        if (clazz == null || exportFields == null || title == null){
            return null;
        }
        ModelAndView mv = null;
        try {
            mv = this.exportXls(request, object, clazz, title);
        } catch (Exception e) {
            log.error("导出初始化失败", e);
        }
        try {
            mv = this.exportXls(request, object, clazz, title);
        } catch (Exception e) {
            log.error("导出失败，title: {}", title, e);
        }
        mv.addObject(NormalExcelConstants.EXPORT_FIELDS, exportFields);
        return mv;
    }

    /**
     * 获取对象ID
     *
     * @return
     */
    private String getId(T item) {
        // 1. 预防 item == null
        if (item == null) {
            log.warn("传入对象为 null，无法获取 ID");
            return null;
        }

        try {
            Object idValue = PropertyUtils.getProperty(item, "id");
            // 2. 预防 idValue == null
            return idValue != null ? idValue.toString() : null;
        } catch (Exception e) {
            log.error("获取对象 {} 的 ID 属性失败", item.getClass().getSimpleName(), e);
            return null;
        }
    }

    /**
     * 通过excel导入数据
     *
     * @param request
     * @param response
     * @return
     */
    protected Result<?> importExcel(HttpServletRequest request, HttpServletResponse response, Class<T> clazz) {
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
        for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
            MultipartFile file = entity.getValue(); // 获取上传文件对象
            if (file == null) {
                continue;
            }
            ImportParams params = new ImportParams();
            params.setTitleRows(2);
            params.setHeadRows(2);
            params.setNeedSave(true);
            try {
                List<T> list = ExcelImportUtil.importExcel(file.getInputStream(), clazz, params);
                if (list.size() == 0) {
                    return Result.error("文件不能为空");
                }
                //update-begin-author:taoyan date:20190528 for:批量插入数据
                long start = System.currentTimeMillis();
                service.saveBatch(list);
                //400条 saveBatch消耗时间1592毫秒  循环插入消耗时间1947毫秒
                //1200条  saveBatch消耗时间3687毫秒 循环插入消耗时间5212毫秒
                log.info("消耗时间" + (System.currentTimeMillis() - start) + "毫秒");
                //update-end-author:taoyan date:20190528 for:批量插入数据
                return Result.ok("文件导入成功！数据行数：" + list.size());
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return Result.error("文件导入失败:" + e.getMessage());
            } finally {
                try {
                    if (file != null) {
                        file.getInputStream().close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return Result.error("文件导入失败！");
    }

    /**
     * 通过excel导入数据
     *
     * @param request
     * @return
     */
    protected Result<?> importExcelNotDuplicates(HttpServletRequest request, Class<T> clazz, String filed) {
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
        for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
            MultipartFile file = entity.getValue();

            if (file == null) {
                continue;
            }

            ImportParams params = new ImportParams();
            params.setTitleRows(2);
            params.setHeadRows(2);
            params.setNeedSave(true);

            try {
                InputStream inputStream = file.getInputStream();
                if (inputStream == null) {
                    return Result.error("文件输入流为空");
                }

                List<T> list = ExcelImportUtil.importExcel(inputStream, clazz, params);
                if (list == null || list.isEmpty()) {
                    return Result.error("文件不能为空");
                }

                List<String> tempList = new ArrayList<>();
                for (T t : list) {
                    if (t == null) continue;

                    if (filed == null || filed.trim().isEmpty()) {
                        return Result.error("字段名不能为空");
                    }
                    // 构造 getter 方法名
                    String methodName = "get" + filed.substring(0, 1).toUpperCase() + filed.substring(1);
                    // 如果字段是布尔类型，可能是 isXXX
                    // 此处假设为 getXXX
                    methodName = underlineToHump(methodName, "_"); // 如果有下划线转驼峰

                    Method getMethod;
                    try {
                        getMethod = clazz.getMethod(methodName);
                    } catch (NoSuchMethodException e) {
                        return Result.error("未找到方法: " + methodName);
                    }

                    Object resultValue;
                    try {
                        resultValue = getMethod.invoke(t);
                    } catch (Exception e) {
                        log.error("调用方法 {} 失败", methodName, e);
                        continue;
                    }

                    String result = resultValue != null ? resultValue.toString() : null;

                    List<T> list1 = result != null ?
                            service.list(new QueryWrapper<T>().eq(filed, result)) :
                            Collections.emptyList();

                    boolean isDuplicateInFile = tempList.stream().anyMatch(x -> x != null && x.equals(result));
                    if (!list1.isEmpty() || isDuplicateInFile) {
                        return Result.error(methodName + "重复！");
                    }

                    if (result != null) {
                        tempList.add(result);
                    }
                }

                long start = System.currentTimeMillis();
                service.saveBatch(list);
                log.info("消耗时间{}毫秒", System.currentTimeMillis() - start);
                return Result.ok("文件导入成功！数据行数：" + list.size());

            } catch (Exception e) {
                log.error("文件导入失败", e);
                return Result.error("文件导入失败:" + e.getMessage());
            } finally {
                if (file != null) {
                    try (InputStream is = file.getInputStream()) {
                        // try-with-resources 自动关闭
                    } catch (IOException e) {
                        log.error("关闭流失败", e);
                    }
                }
            }
        }
        return Result.error("文件导入失败！");
    }

    /**
     * 按照任意字符将字符串转为小驼峰形式
     *
     * @param str
     * @param c   某些字符需要进行转义 例如 \\.
     * @return
     */
    private String underlineToHump(String str, String c) {
        Pattern UNDERLINE_PATTERN = Pattern.compile(c + "([a-z])");
        //正则匹配下划线及后一个字符，删除下划线并将匹配的字符转成大写
        Matcher matcher = UNDERLINE_PATTERN.matcher(str);
        StringBuilder sb = new StringBuilder(str);
        if (matcher.find()) {
            sb = new StringBuilder();
            //将当前匹配的子串替换成指定字符串，并且将替换后的子串及之前到上次匹配的子串之后的字符串添加到StringBuilder对象中
            //正则之前的字符和被替换的字符
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
            //把之后的字符串也添加到StringBuilder对象中
            matcher.appendTail(sb);
        } else {
            //去除除字母之外的前面带的下划线
            return sb.toString().replaceAll(c, "");
        }
        return underlineToHump(sb.toString(), c);
    }
}
